

#include <pit.h>
#include <devmgr.h>
#include <kmalloc.h>
#include <debug.h>
#include <io.h>
#include <process.h>
#include <scheduler.h>

static dword io_ports[4];

dword pit_handle;

static byte pinwheel;
static const char *wheel = "-\\|/";
static dword count;

/**
 * Detect the Programmable Interval Timer (PIT) and add the device structure
 * 
 * returns - error
 */
int detect_pit(void)
{
	int ret = 0;
	
	// assume it exists at the usual location
	
	device_t *dev = (device_t*)kmalloc(sizeof(device_t));
	legacy_locator_t *locator = (legacy_locator_t*)kmalloc(sizeof(legacy_locator_t));
	if ((dev != NULL) && (locator != NULL))
	{
		io_ports[0] = PIT_CHANNEL0;
		io_ports[1] = PIT_CHANNEL1;
		io_ports[2] = PIT_CHANNEL2;
		io_ports[3] = PIT_MODE;
		
		dev->type = DEV_TYPE_LEGACY;
		dev->class = DEV_CLASS_SYSPERIPHERAL;
		dev->id = "PIT";
		dev->gint = 0;  // ?
		dev->locator = locator;
		dev->handle = pit_handle = devmgr_create_handle();
		locator->ports = io_ports;
		locator->count = 4;
		dev->init = init_pit;
		dev->start = start_pit;
		dev->stop = stop_pit;
		dev->handler = pit_isr;
		devmgr_add_device(dev);
	}
	else
	{
		debugf("[PIT] Could not allocate device structure\n");
		ret = PIT_ERR_NOALLOC;
	}
	
	return ret;
}

/**
 * Initializes the PIT
 * 
 * returns - error
 */
int init_pit(void)
{
	int ret = 0;
	
	pit_set_frequency(100);
	pinwheel = 0;
	
	return ret;
}

/**
 * Starts the PIT channel 0 on rate generator
 * 
 * returns - error
 */
int start_pit(void)
{
	int ret = 0;
	
	
	
	return ret;
}

/**
 * Stops the PIT
 * 
 * returns - error
 */
int stop_pit(void)
{
	int ret = 0;
	
	
	return ret;
}

/**
 * Handles PIT interrupts
 *
 * returns - error
 *   ! will return an error if the PIT did not cause the int
 */
int pit_isr(void)
{
	int ret = 0;
	
	count++;
	if (count > 30)
	{
		pinwheel++;
		if (pinwheel > 3)
			pinwheel = 0;
			
		putchar_at(wheel[pinwheel], 7, 79, 0);
		count = 0;
	}
	
	// TODO: update system time
	
	g_current_quantum--;
	
	// check for timers / alarms
	
	// check for sleeping threads
	
	// schedule a new thread
	if (g_current_quantum < 1)
	{
		scheduler(g_current_user_thread, g_current_user_thread->priority);
		dispatcher();
	}
	
	return ret;
}

/**
 * Sets the PIT's interrupt frequency
 * 
 * dword freq - new frequency
 */
void pit_set_frequency(word freq)
{
	int conv = PIT_INTERNAL_FREQ / freq;
	outb(PIT_MODE, 0x36);  // channel 0, rate generator
	outb(PIT_CHANNEL0, conv & 0xFF);  // low byte
	outb(PIT_CHANNEL0, conv >> 8);  // high byte
	
	debugf("[PIT] Clock frequency set %dHz\n", freq);
}
